﻿--[[
   @brief    deal with module descs.
   @author   sogoodchen
   @version  1.0
   @date     2015-03-07
--]]

desc_mgr =
{
    descs = {},
    c_events = {},   -- id={lua_files={file1, file2,...}, callbacks={func1, func2, ...}}
    lua_events = {}, -- id={lua_files={file1, file2,...}, callbacks={func1, func2, ...}}
    services = {}, -- id={lua_files={file1, file2,...}, callbacks={func1, func2, ...}}
    broadcasts = {}, -- id={lua_files={file1, file2,...}, callbacks={func1, func2, ...}}
    service_addrs = {}, -- {cmd = func_addr}
    
    -- 将模块描述添加到descs
    add = function(desc_tbl)
        --base_utility.log('[desc_mgr] add desc module: '..desc_tbl.module_name);
        table.insert(desc_mgr.descs, desc_tbl);
    end;
    
    -- 将模块描述信息中的事件整理到：c_events，lua_events
    arrange_events = function(c_events, lua_events, services, broadcasts)
        for i, module_desc in pairs(desc_mgr.descs) do
            if  type(module_desc) == 'table' and module_desc.module_name ~= nil and module_desc.events ~= nil and type(module_desc.events) == 'table' then
                if nil ~= module_desc.has_arrange and true == module_desc.has_arrange then
                    -- has arrange do nothing
                else
                    --base_utility.log('[desc_mgr] found desc module: '..module_desc.module_name);
                    for j, event_tbl in pairs(module_desc.events) do
                        if type(event_tbl) == 'table' and event_tbl.lua_file ~= nil  then
                            if event_tbl.c_event ~= nil and type(event_tbl.c_event) == 'table' then
                                for k, event_item in pairs(event_tbl.c_event) do
                                    if type(event_item) == 'table' and event_item.id ~= nil and event_item.callback ~= nil then
                                        if c_events[event_item.id] == nil then
                                            c_events[event_item.id] = {lua_files = {},callbacks = {}}
                                        end;
                                        table.insert(c_events[event_item.id].lua_files, event_tbl.lua_file);
                                        table.insert(c_events[event_item.id].callbacks, event_item.callback);
                                    end;
                                end;
                            end;
                            if event_tbl.lua_event ~= nil and type(event_tbl.lua_event) == 'table' then
                                for k, event_item in pairs(event_tbl.lua_event) do
                                    if type(event_item) == 'table' and event_item.id ~= nil and event_item.callback ~= nil then
                                        if lua_events[event_item.id] == nil then
                                            lua_events[event_item.id] = {lua_files = {},callbacks = {}}
                                        end;
                                        table.insert(lua_events[event_item.id].lua_files, event_tbl.lua_file);
                                        table.insert(lua_events[event_item.id].callbacks, event_item.callback);
                                    end;
                                end;
                            end;
                            if event_tbl.reg_service ~= nil and type(event_tbl.reg_service) == 'table' then
                                for k, event_item in pairs(event_tbl.reg_service) do
                                    if type(event_item) == 'table' and event_item.id ~= nil and event_item.callback ~= nil then
                                        if services[event_item.id] == nil then
                                            services[event_item.id] = {lua_files = {},callbacks = {}}
                                        end;
                                        table.insert(services[event_item.id].lua_files, event_tbl.lua_file);
                                        table.insert(services[event_item.id].callbacks, event_item.callback);
                                    end;
                                end;
                            end;
                            if event_tbl.listen_broadcast ~= nil and type(event_tbl.listen_broadcast) == 'table' then
                                for k, event_item in pairs(event_tbl.listen_broadcast) do
                                    if type(event_item) == 'table' and event_item.id ~= nil and event_item.callback ~= nil then
                                        if broadcasts[event_item.id] == nil then
                                            broadcasts[event_item.id] = {lua_files = {},callbacks = {}}
                                        end;
                                        table.insert(broadcasts[event_item.id].lua_files, event_tbl.lua_file);
                                        table.insert(broadcasts[event_item.id].callbacks, event_item.callback);
                                    end;
                                end;
                            end;
                        end;
                    end;
                    module_desc.has_arrange = true;
                end -- has range end
            end;
        end 
    end;

    listen_all_events = function(c_events, lua_events, services, broadcasts)
        base_utility.log('[desc_mgr] listen_all_events');
        local tpfui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        for c_event_id, tbl in pairs(c_events) do
            tpfui:AddLuaListener(c_event_id, "desc_mgr.c_event_callback");
        end;
        
        for lua_event_id, tbl in pairs(lua_events) do
            event_center.add_listener(lua_event_id, "desc_mgr.lua_event_callback");
        end;

        for service_id, tbl in pairs(services) do
            for i, service in ipairs(tbl.callbacks) do
                if service ~= nil then
                    ierd_tgp.social_network.reg_service(service_id, service);
                end
            end
        end;

        for msg_id, tbl in pairs(broadcasts) do 
            for i, func in ipairs(tbl.callbacks) do
                if func ~= nil then
                    if comm_center.is_listened(msg_id) == false then
                        ierd_tgp.social_network.add_msg_handler(msg_id, func);
                    end;
                    comm_center.save_broadcast_name(msg_id, func);
                end
            end
        end;
    end;
    
    -- 整理注册事件
    work = function()
        local c_events = {};
        local lua_events = {};
        local services = {};
        local broadcasts = {};
        desc_mgr.arrange_events(c_events, lua_events, services, broadcasts);
        desc_mgr.listen_all_events(c_events, lua_events, services, broadcasts);
        desc_mgr.merge_event_tb(desc_mgr.c_events, c_events);
        desc_mgr.merge_event_tb(desc_mgr.lua_events, lua_events);
        desc_mgr.merge_event_tb(desc_mgr.services, services);
        desc_mgr.merge_event_tb(desc_mgr.broadcasts, broadcasts);
    end;
    
    -- 核心模块事件回调
    c_event_callback = function(event_id, json_str)
        if desc_mgr.c_events[event_id] == nil then
            return;
        end;
        
        local tpfui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        tpfui:RemoveLuaListener(event_id, "desc_mgr.c_event_callback");
        
        for idx, lua_file in pairs(desc_mgr.c_events[event_id].lua_files) do
            ierd_tgp.tpf_ui.Require(lua_file);
        end;
        
        for idx, callback in pairs(desc_mgr.c_events[event_id].callbacks) do
            g_c_event_callback_tmp_json_param = json_str;
            local script_tmp = callback .. '('..event_id..', g_c_event_callback_tmp_json_param)';
            local func = loadstring(script_tmp);
            func();
            tpfui:AddLuaListener(event_id, callback);
        end;
    end;
    
    -- LUA模块事件回调
    lua_event_callback = function(event_id, json_str)
        if desc_mgr.lua_events[event_id] == nil then
            return;
        end;
        
        event_center.remove_listener(event_id, "desc_mgr.lua_event_callback");
        
        for idx, lua_file in pairs(desc_mgr.lua_events[event_id].lua_files) do
            ierd_tgp.tpf_ui.Require(lua_file);
        end;
        
        local callbacks = desc_mgr.lua_events[event_id].callbacks;
        for idx, callback in pairs(callbacks) do
            g_lua_event_callback_tmp_json_param = json_str;
            local script_tmp = callback .. '(\''..event_id..'\', g_lua_event_callback_tmp_json_param)';
            local func = loadstring(script_tmp);
            func();
            event_center.add_listener(event_id, callback);
        end;
    end;

    merge_event_tb = function (dest, src)
        for k, v in pairs(src) do  
            local dest_v = dest[k];
            if nil == dest_v then
                dest[k] = v;
            else
                local lua_files = dest_v.lua_files;
                if nil == lua_files then
                    dest_v.lua_files = v.lua_files;
                else
                    if type(v.lua_files) == "table" then
                        for i, file in pairs(v.lua_files) do
                            if nil ~= file then
                                table.insert(dest_v.lua_files, file);
                            end
                        end
                    end
                end
                local callbacks = dest_v.callbacks;
                if nil == callbacks then
                    dest_v.callbacks = v.callbacks;
                else
                    if type(v.callbacks) == "table" then
                        for i, callback in pairs(v.callbacks) do
                            if nil ~= callback then
                                table.insert(dest_v.callbacks, callback);
                            end
                        end
                    end
                end
            end
        end  
        return dest;
    end;
};

comm_center =
{
    listened_broadcast = {},
    broadcast_func_names = {},
    broadcast_func_addrs = {},

    reg_service = function(cmd, func)
        if type(func) == "string" then
            ierd_tgp.social_network.reg_service(cmd, func);
        elseif type(func) == "function" then
            desc_mgr.service_addrs[cmd] = func;
            ierd_tgp.social_network.reg_service(cmd, ""); -- 函数名不用给底层，我们自己缓存起来了，通过cmd可以找到函数地址直接调用。
        end;
    end;

    call_service = function(cmd, msg)
        local addr = desc_mgr.service_addrs[cmd];
        if addr ~= nil then
            local tb_output = addr(msg);
            return tb_output;
        end;

        local ret = ierd_tgp.social_network.tagCppStr:new();
        local str_json = base_utility.table_to_json(msg);
        ierd_tgp.social_network.call_service(cmd, str_json, ret);
        local tb_ret = base_utility.json_to_table(ret.str);
        ret:delete();
        return tb_ret;
    end;

    on_call_service = function(cmd_str, func_name, json_str, ret_str)
        if desc_mgr.services[cmd_str] ~= nil then
            for idx, lua_file in pairs(desc_mgr.services[cmd_str].lua_files) do
                ierd_tgp.tpf_ui.Require(lua_file);
            end;
        end;

        local tb_input = base_utility.json_to_table(json_str);

        local addr = desc_mgr.service_addrs[cmd_str];
        if addr ~= nil then
            local tb_output = addr(tb_input);
            ret_str.str = base_utility.table_to_json(tb_output);
            return;
        end;

        local script = "function call_service_internal(input) local v = "..func_name.."(input) return v end";
        assert(loadstring(script))();
        local msg = call_service_internal(tb_input);

        ret_str.str = base_utility.table_to_json(msg);
    end;

    listen_broadcast = function(cmd, func)
        if type(func) == "string" then
            if comm_center.is_listened(cmd) == false then
                ierd_tgp.social_network.add_msg_handler(cmd, func);
            end;
            comm_center.save_broadcast_name(cmd, func);
        elseif type(func) == "function" then
            if comm_center.is_listened(cmd) == false then
                ierd_tgp.social_network.add_msg_handler(cmd, ""); -- 函数名不用给底层，我们自己缓存起来了，通过cmd可以找到函数地址直接调用。
            end;
            comm_center.save_broadcast_addr(cmd, func);
        end;
    end;

    send_broadcast = function(cmd, msg)
        local str_json = base_utility.table_to_json(msg);
        ierd_tgp.social_network.post_msg(cmd, str_json);
    end;
    
    on_process_msg = function(cmd_str, func_name_no_use, json_str)
        if desc_mgr.broadcasts[cmd_str] ~= nil and desc_mgr.broadcasts[cmd_str].lua_files ~= nil then
            for idx, lua_file in pairs(desc_mgr.broadcasts[cmd_str].lua_files) do
                ierd_tgp.tpf_ui.Require(lua_file);
            end;
        end

        local tb_input = base_utility.json_to_table(json_str);
        if nil ~= tb_input and 'table' == type(tb_input) then
            tb_input["cmd"] = cmd_str;
        end

        if tb_input ~= nil and 'table' ~= type(tb_input) then
            tb_input = {};
            table.insert(tb_input, "not_table");
        end;

        if tb_input == nil then
            tb_input = {};
            table.insert(tb_input, "not_table");
        end;

        if comm_center.broadcast_func_names[cmd_str] ~= nil then
            for i, func_name in ipairs(comm_center.broadcast_func_names[cmd_str]) do
                local script = "function process_msg_internal(input) local v = "..func_name.."(input) return v end";
                assert(loadstring(script))();
                process_msg_internal(tb_input);
            end
        end

        if comm_center.broadcast_func_addrs[cmd_str] ~= nil then
            for i, func_addr in ipairs(comm_center.broadcast_func_addrs[cmd_str]) do
                if func_addr ~= nil then
                    func_addr(tb_input);
                end
            end
        end
    end;

    is_listened = function(id)
        if comm_center.listened_broadcast[id] == nil then
            return false;
        else
            return true;
        end
    end;

    save_broadcast_addr = function(id, addr)
        comm_center.listened_broadcast[id] = 1;
        if comm_center.broadcast_func_addrs[id] == nil then
            comm_center.broadcast_func_addrs[id] = {};
        end

        table.insert(comm_center.broadcast_func_addrs[id], addr);
    end;

    save_broadcast_name = function (id, name)
        comm_center.listened_broadcast[id] = 1;
        if comm_center.broadcast_func_names[id] == nil then
            comm_center.broadcast_func_names[id] = {};
        end

        table.insert(comm_center.broadcast_func_names[id], name);
    end;

    call_service_asyn = function(cmd, msg, callback)
        local ret = ierd_tgp.social_network.tagCppStr:new();
        msg["seq_num_"] = 0;
        local str_json = base_utility.table_to_json(msg);
        ierd_tgp.social_network.call_service_asyn(cmd, str_json, callback, ret);
        local tb_ret = base_utility.json_to_table(ret.str);
        ret:delete();
        return tb_ret;
    end;

    call_service_asyn_v2 = function(cmd, msg, callback, callback_in_main_thread)
        local str_json = base_utility.table_to_json(msg);
        ierd_tgp.social_network.call_service_asyn_v2(cmd, str_json, callback, callback_in_main_thread);
    end;
};